﻿using System.Threading;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;
using UnityDemo.DemoCode.LifeTime;
using UnityProvider;

namespace UnityDemo.DemoCode
{
    //http://msdn.microsoft.com/en-us/library/ff647854.aspx
    //http://www.ladislavmrnka.com/2011/03/unity-build-in-lifetime-managers/
    //http://msdn.microsoft.com/en-us/library/microsoft.practices.unity.lifetimemanager.aspx 
    //1.Transient                      http://msdn.microsoft.com/en-us/library/microsoft.practices.unity.transientlifetimemanager.aspx
    //2.ContainerControled             
    //3.ExternallyControled            http://msdn.microsoft.com/en-us/library/microsoft.practices.unity.externallycontrolledlifetimemanager.aspx
    //4.PerThread                      http://msdn.microsoft.com/en-us/library/microsoft.practices.unity.perthreadlifetimemanager.aspx
    //5.SynchronizedLifeTimeManager    http://msdn.microsoft.com/en-us/library/microsoft.practices.unity.synchronizedlifetimemanager.aspx
    //6/HierarchicalLifetimeManager    http://msdn.microsoft.com/en-us/library/ff660872(v=PandP.20).aspx
    public class LifeTimeDemo : IDemo
    {
        // In this sample I've used Printed the hash code of each instance to show its uniqueness
        public LifeTimeDemo()
        {
            UnityInstanceProvider.ApplicationWideUnityInstance.RegisterType<SingletonInstance>(new ContainerControlledLifetimeManager());
            UnityInstanceProvider.ApplicationWideUnityInstance.RegisterType<PerInstance>();
            UnityInstanceProvider.ApplicationWideUnityInstance.RegisterType<ExternalInstance>(new ExternallyControlledLifetimeManager());
            UnityInstanceProvider.ApplicationWideUnityInstance.RegisterType<PerThredInstance>(new PerThreadLifetimeManager());
            UnityInstanceProvider.ApplicationWideUnityInstance.RegisterType<HierarchicalLifetimeInstance>(new HierarchicalLifetimeManager());

            TransientLifetimeManagerDemo();
            ContainerConrtolledLifeTimeManagerDemo();
            ExternallyControlledLifetimeManager();
            PerThreadLifetimeManagerDemo();
            HierarchicalLifetimeManagerDemo();
        }


        //When you register an existing object using the RegisterInstance method, the default behavior is 
        //for the container to take over management of the lifetime of the object you pass to this method using 
        //the ContainerControlledLifetimeManager. This means that the existing object remains in scope as long as 
        //the container is in scope, and it is disposed when the container goes out of scope and is garbage-collected or
        //when code explicitly disposes the container. You can also use this lifetime manager with the RegisterType method 
        //to specify that Unity should manage the object as a singleton instance.
        private void TransientLifetimeManagerDemo()
        {

            var perInstance1 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<PerInstance>();
            perInstance1.PrintHashCode();

            var perInstance2 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<PerInstance>();
            perInstance2.PrintHashCode();
        }


        //ContainerControlledLifetimeManager. Unity returns the same instance of the registered type or object 
        //each time you call the Resolve or ResolveAll method or when the dependency mechanism injects 
        //instances into other classes. This lifetime manager effectively implements a singleton behavior for objects. 
        //Unity uses this lifetime manager by default for the RegisterInstance method if you do not specify a different 
        //lifetime manager. If you want singleton behavior for an object that Unity creates when you use 
        //the RegisterType method, you must explicitly specify this lifetime manager. 
        //The behavior is as follows: If you used the RegisterType method to register a type, 
        //Unity creates a new instance of the registered type during the first call to the Resolve or ResolveAll
        //method or when the dependency mechanism injects instances into other classes. 
        //Subsequent requests return the same instance.
        //If you used the RegisterInstance method to register an existing object, 
        //Unity returns this instance every time you call the Resolve or ResolveAll method or
        //when the dependency mechanism injects instances into other classes.
        private void ContainerConrtolledLifeTimeManagerDemo()
        {
            var singleInstance1 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<SingletonInstance>();
            singleInstance1.PrintHashCode();

            var singleInstance2 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<SingletonInstance>();
            singleInstance2.PrintHashCode();

        }


        //ExternallyControlledLifetimeManager. This lifetime manager allows you to register type mappings and existing 
        //objects with the container so that it maintains only a weak reference to the objects it creates when you call
        //the Resolve or ResolveAll method or when the dependency mechanism injects instances into other classes based 
        //on attributes or constructor parameters within that class. Unity returns the same instance of the registered 
        //type or object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects 
        //instances into other classes. However, the container does not hold onto a strong reference to the object after 
        //it creates it, which means that the garbage collector can dispose of the object if no other code is holding a 
        //strong reference to it.
        private void ExternallyControlledLifetimeManager()
        {
            var externalInstance1 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<ExternalInstance>();
            externalInstance1.PrintHashCode();

            var externalInstance2 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<ExternalInstance>();
            externalInstance2.PrintHashCode();
        }

        //  PerThreadLifetimeManager. Unity returns, on a per-thread basis, the same instance of the registered type or 
        //object each time you call the Resolve or ResolveAll method or when the dependency mechanism injects instances 
        //into other classes. This lifetime manager effectively implements a singleton behavior for objects on a per-thread
        //basis. PerThreadLifetimeManager returns different objects from the container for each thread. 
        //The behavior is as follows: If you used the RegisterType method to register a type, Unity creates a new 
        //instance of the registered type the first time the type is resolved in a specified thread, either to answer a 
        //call to the Resolve or ResolveAll methods for the registered type or to fulfill a dependency while resolving a 
        //different type. Subsequent resolutions on the same thread return the same instance.
        //Using the RegisterInstance method to register an existing object results in the same behavior as if you 
        //just registered the lifetime container with RegisterType. Therefore, it is recommended that you do not use 
        //the RegisterInstance method to register an existing object when using the PerThreadLifetimeManager.
        //PerThreadLifetimeManager returns the object desired or permits the container to create a new instance
        //if no such object is currently stored for the current thread. A new instance is also created if called 
        //on a different thread than the one that set the value. This lifetime manager does not dispose the instances
        //it holds.
        private void PerThreadLifetimeManagerDemo()
        {
            var threadInstance1 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<PerThredInstance>();
            threadInstance1.PrintHashCode();
            var threadInstance2 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<PerThredInstance>();
            threadInstance2.PrintHashCode();

            Task.Factory.StartNew(() => ThreadCalledMethod(1));
            Task.Factory.StartNew(() => ThreadCalledMethod(2));
        }


        private void ThreadCalledMethod(int threadNumber)
        {
            LoggingUtility.LogerInstance.Logger.Info("Current thread number =" + threadNumber);
            var threadInstance1 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<PerThredInstance>();
            threadInstance1.PrintHashCode(threadNumber);
            var threadInstance2 = UnityInstanceProvider.ApplicationWideUnityInstance.Resolve<PerThredInstance>();
            threadInstance2.PrintHashCode(threadNumber);
        }

        //For this lifetime manager, as for the ContainerControlledLifetimeManager, Unity returns 
        //the same instance of the registered type or object each time you call the Resolve or ResolveAll 
        //method or when the dependency mechanism injects instances into other classes. The distinction is
        //that when there are child containers, each child resolves its own instance of the object and does not 
        //share one with the parent. When resolving in the parent, the behavior is like a container controlled 
        //lifetime; when resolving the parent and the child you have different instances with each acting as a 
        //container-controlled lifetime. If you have multiple children, each will resolve its own instance. 
        private void HierarchicalLifetimeManagerDemo()
        {
            var parentContainer = UnityInstanceProvider.ApplicationWideUnityInstance;
            var instance1 = parentContainer.Resolve<HierarchicalLifetimeInstance>();
            instance1.PrintHashCode();
            
            IUnityContainer childContiner = parentContainer.CreateChildContainer();

            var instance2 = childContiner.Resolve<HierarchicalLifetimeInstance>();
            instance2.PrintHashCode();
        }


    }
}